﻿using System;
using System.Net;
using System.Text;

namespace Toybox.NicoSharp.Video
{
	using Extensions;
	using Message;
	using Utilities;

	/// <summary>
	/// コメントの種類
	/// </summary>
	[Flags]
	public enum ThreadTypes : byte
	{
		/// <summary>
		/// 投稿者コメント
		/// </summary>
		Owner = 0x01,

		/// <summary>
		/// 一般ユーザーコメント
		/// </summary>
		User = 0x02,

		/// <summary>
		/// コミュニティコメント
		/// </summary>
		Community = 0x04,

		/// <summary>
		/// ニコスクリプト
		/// </summary>
		Script = 0x08,

		/// <summary>
		/// すべて
		/// </summary>
		All = Owner | User | Community | Script,
	}

	public partial class Flv
	{

		#region [Static]

		public static Flv Get(string id, CookieCollection cookies = null)
		{
			Flv flv = new Flv();
			flv.Cookies = cookies.Coerce();

			string url = NicoUrls.FlApi + "getflv";
			string html = NicoWebClient.Post(url, "v=" + id, flv.Cookies);

			#region 格納

			foreach (var kvp in Deserializer.AndEqualPairDeserializer(html))
			{
				switch (kvp.Key)
				{
					// スレッド
					case "thread_id":
						flv.thread_id = kvp.Value; break;
					case "optional_thread_id":
						flv.optional_thread_id = kvp.Value; break;
					case "nicos_id":
						flv.nicos_id = kvp.Value; break;

					case "l":
						flv.l = int.Parse(kvp.Value); break;
					case "url":
						flv.url = Uri.UnescapeDataString(kvp.Value); break;
					case "link":
						flv.link = Uri.UnescapeDataString(kvp.Value); break;
					case "ms":
						flv.ms = Uri.UnescapeDataString(kvp.Value); break;
					case "user_id":
						flv.user_id = kvp.Value; break;
					case "is_premium":
						flv.is_premium = (Message.PremiumType)int.Parse(kvp.Value); break;
					case "nickname":
						flv.nickname = Uri.UnescapeDataString(kvp.Value); break;
					case "time":
						flv.time = long.Parse(kvp.Value); break;
					case "done":
						flv.done = bool.Parse(kvp.Value); break;
					case "needs_key":
						flv.needs_key = kvp.Value == "1"; break;
					case "feedrev":
						flv.feedrev = kvp.Value; break;
					case "ng_rv":
						flv.ng_rv = kvp.Value; break;
					case "ng_up":
						flv.ng_up = Uri.UnescapeDataString(Uri.UnescapeDataString(kvp.Value)); break;

					case "hms":
						flv.hms = kvp.Value; break;
					case "hmsp":
						flv.hmsp = int.Parse(kvp.Value); break;
					case "hmst":
						flv.hmst = int.Parse(kvp.Value); break;
					case "hmstk":
						flv.hmstk = kvp.Value; break;

					case "rpu":
						flv.rpu = RPU.Deserialize(Uri.UnescapeDataString(kvp.Value)); break;

					case "closed":
						flv.closed = kvp.Value == "1"; break;
					case "error":
						flv.error = kvp.Value; break;
				}
			}

			#endregion

			#region コメント情報

			// 投稿者コメントの判断基準ってなに？
			flv.ThreadTypes = ThreadTypes.User | ThreadTypes.Owner;
			if (flv.needs_key)
			{
				flv.ThreadTypes |= ThreadTypes.Community;
			}
			if (!string.IsNullOrEmpty(flv.nicos_id))
			{
				flv.ThreadTypes |= ThreadTypes.Script;
			}

			#endregion

			return flv;
		}

		#endregion [Static]


		#region Events

		public event EventHandler<Packet> PostPacketCompleted;

		protected virtual void OnPostPacketCompleted(Packet packet)
		{
			if (this.PostPacketCompleted != null)
				this.PostPacketCompleted(this, packet);
		}

		#endregion Events


		#region Public Members

		public CookieCollection Cookies
		{
			get;
			protected set;
		}

		public string GeneralThreadId
		{
			get { return needs_key ? this.optional_thread_id : this.thread_id; }
		}

		public string CommunityThreadId
		{
			get { return needs_key ? this.thread_id : null; }
		}

		public string ScriptThreadId
		{
			get { return this.nicos_id; }
		}

		public ThreadTypes ThreadTypes
		{
			get;
			protected set;
		}

		#region Flv Members

		// スレッド
		/// <summary>
		/// ThreadID
		/// </summary>
		public string thread_id;

		/// <summary>
		/// ニコスクリプトThreadID
		/// </summary>
		public string nicos_id;

		/// <summary>
		/// 付加ThreadID
		/// </summary>
		public string optional_thread_id;

		/// <summary>
		/// 再生時間
		/// </summary>
		public int l;

		public string url;
		public string link;
		public string ms;

		/// <summary>
		/// ユーザID
		/// </summary>
		public string user_id;

		/// <summary>
		/// プレミアムタイプ
		/// </summary>
		public Message.PremiumType is_premium;

		/// <summary>
		/// ユーザ名
		/// </summary>
		public string nickname;

		public long time;
		public bool done;
		public bool needs_key = false;
		public string feedrev;

		// NG
		public string ng_rv;
		public string ng_up;

		// 広場関連
		public string hms;
		public int hmsp;
		public int hmst;
		public string hmstk;

		public RPU rpu;

		/// <summary>
		/// 非公開かどうか
		/// </summary>
		public bool closed;

		/// <summary>
		/// エラー
		/// </summary>
		public string error;

		#endregion Flv Members

		#endregion  Public Members


		#region Public Methods

		public Packet PostPacket(string packet)
		{
			string xml = NicoWebClient.Post(this.ms, packet, this.Cookies);
			return Packet.Deserialize(xml);
		}

		public void PostPacketAsync(string packet)
		{
			var wc = new NicoWebClient();
			wc.UploadStringCompleted += wc_UploadStringCompleted;
			wc.Encoding = Encoding.UTF8;
			wc.CookieContainer.Add(this.Cookies);

			wc.UploadStringAsync(new Uri(this.ms), packet);
		}

		public ChatResult PostChat(string chat)
		{
			if (string.IsNullOrEmpty(chat))
				throw new ArgumentNullException("chat");

			string xml = NicoWebClient.Post(this.ms, chat, this.Cookies);
			Packet packet = Packet.Deserialize(xml);
			ChatResult result = packet.chat_result;

			return result;
		}

		#endregion Public Methods

		#region Private Methods

		private void wc_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
		{
			NicoWebClient wc = sender as NicoWebClient;
			wc.Dispose();

			this.OnPostPacketCompleted(Packet.Deserialize(e.Result));
		}

		#endregion Private Methods

	}
}
